package com.xuxueli.applyModules.utils;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.util.DateUtils;
import com.xuxueli.applyModules.entity.GraduaterNamePara;
import com.xuxueli.applyModules.exception.ErrorException;
import com.xuxueli.applyModules.utils.testcharts.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.*;

/**
 * 高性能处理Excel工具类
 */
@Slf4j
public class EasyExcelUtil {

    //下载模板
    public static void downloadExcel(HttpServletRequest request, HttpServletResponse response){
        try {
            //文件名称
            String fileName = "importStaffRoster.xlsx";
            //设置要下载的文件的名称
            response.setHeader("Content-disposition", "attachment;fileName=" + fileName);
            //通知客服文件的MIME类型
            response.setContentType("application/vnd.ms-excel;charset=UTF-8");
            //获取文件的路径
            String filePath = FileUtil.class.getResource("/template/" + fileName).getPath();
            FileInputStream input = new FileInputStream(filePath);
            OutputStream out = response.getOutputStream();
            byte[] b = new byte[2048];
            int len;
            while ((len = input.read(b)) != -1) {
                out.write(b, 0, len);
            }
            //修正 Excel在“xxx.xlsx”中发现不可读取的内容。是否恢复此工作薄的内容？如果信任此工作簿的来源，请点击"是"
            response.setHeader("Content-Length", String.valueOf(input.getChannel().size()));
            input.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }

    /**
     * 使用 模型 来读取Excel
     *
     * @param inputStream Excel的输入流
     * @param clazz       模型的类
     * @return 返回 模型 的列表
     */
    public static <T> List<T> readExcel(InputStream inputStream, Class<T> clazz) {
        ModelExcelListener<T> listener = new ModelExcelListener<T>();
        EasyExcel.read(inputStream, clazz,listener).sheet(0).doReadSync();
        //EasyExcel.read(inputStream, clazz, listener).sheet().doRead();
        return listener.getDatas();
    }

    /**
     * 使用 模型 来读取Excel
     *
     * @param inputStream Excel的输入流
     * @param clazz       模型的类
     * @return 返回 模型 的列表
     */
    public static <T> List<T> readExcelBySheet(InputStream inputStream, Class<T> clazz,Integer sheetNo) {
        ModelExcelListener<T> listener = new ModelExcelListener<T>();
        EasyExcel.read(inputStream, clazz,listener).sheet(sheetNo).doReadSync();
        //EasyExcel.read(inputStream, clazz, listener).sheet().doRead();
        return listener.getDatas();
    }

    /**
     * 使用 模型 来导出到WEB
     *
     * @param response      web的响应
     * @param data         要写入的以 模型 为单位的数据
     * @param fileName     配置Excel的表名
     * @param sheetName    配置Excel的页签名
     * @param clazz        模型的类
     * @throws IOException
     */
    public static <T> void writeExcel(HttpServletResponse response, List<T> data, Class<T> clazz, String fileName, String sheetName) throws IOException {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        if(StringUtils.isBlank(fileName)){
            fileName = URLEncoder.encode(String.format("%s测试.", DateUtils.format(new Date(), "yyyy-MM-dd")), "UTF-8");

        }
        response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ExcelTypeEnum.XLSX);
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), clazz).sheet(sheetName).doWrite(data);
    }

    /**
     * 使用 模型 来写入Excel
     * <br/>注意，如果是web输出流，需要设置头
     *
     * @param outputStream Excel的输出流
     * @param data         要写入的以 模型 为单位的数据
     * @param sheetName    配置Excel的表名字
     * @param clazz        模型的类
     */
    public static <T> void writeExcel(OutputStream outputStream, List<T> data, Class<T> clazz, String sheetName) {
        EasyExcel.write(outputStream, clazz).sheet(sheetName).doWrite(data);
    }

    /**
     * * 使用 模型 来写入Excel
     * @param file
     * @param data
     * @param clazz
     * @param sheetName
     * @param <T>
     */
    public static <T> void writeExcelFile(File file, List<T> data, Class<T> clazz, String sheetName) {
        EasyExcel.write(file,clazz).sheet(sheetName).doWrite(data);
    }

    /**
     * 模型 解析监听器
     */
    private static class ModelExcelListener<T> extends AnalysisEventListener<T> {
        private List<T> datas = new ArrayList<>();

        @Override
        public void invoke(T object, AnalysisContext context) {
            datas.add(object);
        }

        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            //datas.clear();
        }

        public List<T> getDatas() {
            return datas;
        }
    }


    /**
     * 待测试
     * @param filePath
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> List<T> read(String filePath, final Class<?> clazz) {
        File f = new File(filePath);
        try (FileInputStream fis = new FileInputStream(f)) {
            return read(fis, clazz);
        } catch (FileNotFoundException e) {
            log.error("文件{}不存在", filePath, e);
        } catch (IOException e) {
            log.error("文件读取出错", e);
        }
        return null;
    }

    public static <T> List<T> read(InputStream inputStream, final Class<?> clazz) {
        if (inputStream == null) {
            throw new ErrorException(500,"解析出错了，文件流是null");
        }
        // 有个很重要的点 DataListener 不能被spring管理，要每次读取excel都要new,然后里面用到spring可以构造方法传进去
        DataListener<T> listener = new DataListener<>();
        // 这里 需要指定读用哪个class去读，然后读取第一个sheet 文件流会自动关闭
        EasyExcel.read(inputStream, clazz, listener).sheet().doRead();
        return listener.getRows();
    }

    public static void write(String outFile, List<?> list) {
        Class<?> clazz = list.get(0).getClass();
        EasyExcel.write(outFile, clazz).sheet().doWrite(list);
    }

    public static void write(String outFile, List<?> list, String sheetName) {
        Class<?> clazz = list.get(0).getClass();
        EasyExcel.write(outFile, clazz).sheet(sheetName).doWrite(list);
    }

    public static void write(OutputStream outputStream, List<?> list, String sheetName) {
        Class<?> clazz = list.get(0).getClass();
        // sheetName为sheet的名字，默认写第一个sheet
        EasyExcel.write(outputStream, clazz).sheet(sheetName).doWrite(list);
    }

    /**
     * 文件下载（失败了会返回一个有部分数据的Excel），用于直接把excel返回到浏览器下载
     */
    public static void download(HttpServletResponse response, List<?> list, String sheetName) throws IOException {
        Class<?> clazz = list.get(0).getClass();
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName = URLEncoder.encode(sheetName, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), clazz).head(clazz).sheet(sheetName).doWrite(list);
    }

    private static class DataListener<T> extends AnalysisEventListener<T> {

        private final List<T> rows = new ArrayList<>();

        @Override
        public void invoke(T t, AnalysisContext analysisContext) {
            rows.add(t);
        }

        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            log.info("解析完成！读取{}行", rows.size());
        }

        public List<T> getRows() {
            return rows;
        }
    }

    /**
     * 测试easyexcel导出本地模板
     * @param args
     */
    public static void main(String[] args) {
        List<GraduaterNamePara> dateList= new ArrayList<>();
        String fileName = "D:\\downfiles\\write.xlsx";
        //排除掉不想输出的列 假设我们要忽略 stringData
        Set<String> excludeColumnFiledNames = new HashSet<String>();
        excludeColumnFiledNames.add("telephone");
        EasyExcel.write(fileName, GraduaterNamePara.class).excludeColumnFiledNames(excludeColumnFiledNames).sheet("模板").doWrite(dateList);

        String fileNames = "D:\\downfiles\\writes.xlsx";
        //只输出实体类我们想要的字段  假设我们只要导出 stringData
        Set<String> includeColumnFiledNames = new HashSet<String>();
        includeColumnFiledNames.add("telephone");
        EasyExcel.write(fileNames, GraduaterNamePara.class).includeColumnFiledNames(includeColumnFiledNames).sheet("模板").doWrite(dateList);


    }



}
